﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YJKArchUtils.Utils;
using YJKElementFinish.ColumnSurface.Managers;

namespace YJKElementFinish.MultiWallEdit.Controller
{
	public class WallGroupingController
	{
		public WallGroupingController(ExternalCommandData cmdData)
		{
			this.m_ComdData = cmdData;
			this.m_UIApp = cmdData.Application;
			this.m_UIDoc = this.m_UIApp.ActiveUIDocument;
			this.m_App = this.m_UIApp.Application;
			this.m_Doc = this.m_UIDoc.Document;
		}

		public void WallGrouping(List<Wall> selWalls, ref List<List<Wall>> linearWalls, ref List<List<Wall>> arcWalls, ref List<Wall> otherWalls)
		{
			Dictionary<XYZ, List<Wall>> dictionary = new Dictionary<XYZ, List<Wall>>(new ExtendXYZComparer(4));
			Dictionary<XYZ, List<Wall>> dictionary2 = new Dictionary<XYZ, List<Wall>>(new ExtendXYZComparer(4));
			foreach (Wall wall in selWalls)
			{
				Curve curve = (wall.Location as LocationCurve).Curve;
				if (curve.GetType() == typeof(Line))
				{
					XYZ direction = (curve as Line).Direction;
					if (dictionary.ContainsKey(direction))
					{
						dictionary[direction].Add(wall);
					}
					else if (dictionary.ContainsKey(-direction))
					{
						this.FlipWall(wall);
						dictionary[-direction].Add(wall);
					}
					else
					{
						dictionary.Add(direction, new List<Wall>
						{
							wall
						});
					}
				}
				else
				{
					XYZ center = (curve as Arc).Center;
					if (!dictionary2.ContainsKey(center))
					{
						dictionary2.Add(center, new List<Wall>
						{
							wall
						});
					}
					else
					{
						dictionary2[center].Add(wall);
					}
				}
			}
			this.LinearWallGroup(dictionary, ref linearWalls);
			this.ArcWallGroup(dictionary2, ref arcWalls);
			List<Wall> list = new List<Wall>();
			foreach (List<Wall> collection in linearWalls)
			{
				list.AddRange(collection);
			}
			foreach (List<Wall> collection2 in arcWalls)
			{
				list.AddRange(collection2);
			}
			otherWalls = selWalls.Except(list).ToList<Wall>();
		}

		private void FlipWall(Wall wall)
		{
			Curve curve = (wall.Location as LocationCurve).Curve;
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_Doc);
				transaction.Start("flip wall");
				WallUtils.DisallowWallJoinAtEnd(wall, 0);
				WallUtils.DisallowWallJoinAtEnd(wall, 1);
				if (curve.GetType() == typeof(Line))
				{
					Line line = curve as Line;
					(wall.Location as LocationCurve).Curve = line.CreateReversed();
					wall.Flip();
				}
				else if (curve.GetType() == typeof(Arc))
				{
					Arc arc = curve as Arc;
					(wall.Location as LocationCurve).Curve = arc.CreateReversed();
					wall.Flip();
				}
				WallUtils.AllowWallJoinAtEnd(wall, 0);
				WallUtils.AllowWallJoinAtEnd(wall, 1);
				transaction.Commit();
			}
			catch
			{
				if (transaction != null)
				{
					transaction.RollBack();
				}
			}
		}

		public void GetWallTypeGroup(List<List<Wall>> wallGroups, ref List<List<WallType>> wallGroupTypes, ref List<KeyValuePair<int, bool>> groupIndexWithWall)
		{
			foreach (List<Wall> list in wallGroups)
			{
				List<WallType> wallTypes = new List<WallType>();
				foreach (Wall wall in list)
				{
					wallTypes.Add(wall.WallType);
				}
				bool isReverse = false;
				int num = wallGroupTypes.FindIndex((List<WallType> item) => this.IsEqualWidthwallTypes(wallTypes, item, ref isReverse));
				if (num == -1)
				{
					wallGroupTypes.Add(wallTypes);
					KeyValuePair<int, bool> item3 = new KeyValuePair<int, bool>(wallGroupTypes.Count - 1, isReverse);
					groupIndexWithWall.Add(item3);
				}
				else
				{
					KeyValuePair<int, bool> item2 = new KeyValuePair<int, bool>(num, isReverse);
					groupIndexWithWall.Add(item2);
				}
			}
		}

		private bool IsEqualWidthwallTypes(List<WallType> types1, List<WallType> types2, ref bool isReverse)
		{
			if (types1.Count != types2.Count)
			{
				return false;
			}
			bool flag = true;
			for (int i = 0; i < types1.Count; i++)
			{
				if (types1[i].Name.CompareTo(types2[i].Name) != 0)
				{
					flag = false;
					break;
				}
			}
			if (flag)
			{
				isReverse = false;
				return true;
			}
			types1.Reverse();
			for (int i = 0; i < types1.Count; i++)
			{
				if (types1[i].Name.CompareTo(types2[i].Name) != 0)
				{
					return false;
				}
			}
			isReverse = true;
			return false;
		}

		private void LinearWallGroup(Dictionary<XYZ, List<Wall>> parallelLinearWall, ref List<List<Wall>> linearWalls)
		{
			foreach (KeyValuePair<XYZ, List<Wall>> keyValuePair in parallelLinearWall)
			{
				XYZ xyz = XYZ.Zero + Geometry.RotateTo(keyValuePair.Key, Math.PI*.5, XYZ.BasisZ) * 10000.0;
				Line line = Line.CreateBound(XYZ.Zero, xyz);
				List<Wall> value = keyValuePair.Value;
				List<KeyValuePair<XYZ, Wall>> list = new List<KeyValuePair<XYZ, Wall>>();
				foreach (Wall wall in value)
				{
					Line line2 = (wall.Location as LocationCurve).Curve as Line;
					XYZ key = Geometry.CalculateFootPoint(line, line2.GetEndPoint(0));
					KeyValuePair<XYZ, Wall> item = new KeyValuePair<XYZ, Wall>(key, wall);
					list.Add(item);
				}
				list.Sort(new ParallelLinearWallComparer(xyz));
				if (list.Count > 1 && !Geometry.IsEqual((list[list.Count - 1].Key - list[0].Key).Normalize(), list[0].Value.Orientation))
				{
					list.Reverse();
				}
				List<List<KeyValuePair<XYZ, Wall>>> list2 = new List<List<KeyValuePair<XYZ, Wall>>>();
				while (list.Count > 0)
				{
					List<int> list3 = new List<int>();
					list3.Add(0);
					this.FindLinkedLinearWalls(0, 1, list, ref list3);
					List<KeyValuePair<XYZ, Wall>> list4 = new List<KeyValuePair<XYZ, Wall>>();
					for (int i = list3.Count - 1; i >= 0; i--)
					{
						int index = list3[i];
						list4.Insert(0, list[index]);
						list.RemoveAt(index);
					}
					if (list4.Count > 1)
					{
						list2.Add(list4);
					}
				}
				foreach (List<KeyValuePair<XYZ, Wall>> list5 in list2)
				{
					List<Wall> list6 = new List<Wall>();
					foreach (KeyValuePair<XYZ, Wall> keyValuePair2 in list5)
					{
						list6.Add(keyValuePair2.Value);
					}
					linearWalls.Add(list6);
				}
			}
		}

		private void FindLinkedLinearWalls(int currentIndex, int findIndex, List<KeyValuePair<XYZ, Wall>> sortWallList, ref List<int> findIndexList)
		{
			if (currentIndex >= sortWallList.Count - 1 || findIndex >= sortWallList.Count)
			{
				return;
			}
			double val = sortWallList[findIndex].Key.DistanceTo(sortWallList[currentIndex].Key);
			double val2 = (sortWallList[findIndex].Value.Width + sortWallList[currentIndex].Value.Width) / 2.0;
			if (!Geometry.IsEqual(val, val2))
			{
				this.FindLinkedLinearWalls(currentIndex, findIndex + 1, sortWallList, ref findIndexList);
				return;
			}
			Line line = (sortWallList[currentIndex].Value.Location as LocationCurve).Curve as Line;
			Line line2 = (sortWallList[findIndex].Value.Location as LocationCurve).Curve as Line;
			XYZ pt = Geometry.CalculateFootPoint(line, line2.GetEndPoint(0));
			XYZ pt2 = Geometry.CalculateFootPoint(line, line2.GetEndPoint(1));
			XYZ xyz = null;
			XYZ xyz2 = null;
			Geometry.OverlapType overlapType = Geometry.IsOverlapOfTwoLine(this.To2D(pt), this.To2D(pt2), this.To2D(line.GetEndPoint(0)), this.To2D(line.GetEndPoint(1)), ref xyz, ref xyz2, true, 1E-09);
			if (overlapType == Geometry.OverlapType._OT_None || overlapType == Geometry.OverlapType._OT_Linked)
			{
				this.FindLinkedLinearWalls(currentIndex, findIndex + 1, sortWallList, ref findIndexList);
				return;
			}
			findIndexList.Add(findIndex);
			this.FindLinkedLinearWalls(findIndex, findIndex + 1, sortWallList, ref findIndexList);
		}

		private XYZ To2D(XYZ pt)
		{
			return new XYZ(pt.X, pt.Y, 0.0);
		}

		private void ArcWallGroup(Dictionary<XYZ, List<Wall>> parallelArcWall, ref List<List<Wall>> arcWalls)
		{
			foreach (KeyValuePair<XYZ, List<Wall>> keyValuePair in parallelArcWall)
			{
				List<Wall> value = keyValuePair.Value;
				List<KeyValuePair<double, Wall>> list = new List<KeyValuePair<double, Wall>>();
				foreach (Wall wall in value)
				{
					Arc arc = (wall.Location as LocationCurve).Curve as Arc;
					KeyValuePair<double, Wall> item = new KeyValuePair<double, Wall>(arc.Radius, wall);
					list.Add(item);
				}
				list.Sort(new ParallelArcWallComparer());
				if (list.Count > 1)
				{
					Arc arc2 = (list[0].Value.Location as LocationCurve).Curve as Arc;
					if (!Geometry.IsEqual((arc2.GetEndPoint(0) - arc2.Center).Normalize(), list[0].Value.Orientation))
					{
						list.Reverse();
					}
				}
				List<List<KeyValuePair<double, Wall>>> list2 = new List<List<KeyValuePair<double, Wall>>>();
				while (list.Count > 0)
				{
					List<int> list3 = new List<int>();
					list3.Add(0);
					this.FindLinkedArcWalls(0, 1, list, ref list3);
					List<KeyValuePair<double, Wall>> list4 = new List<KeyValuePair<double, Wall>>();
					for (int i = list3.Count - 1; i >= 0; i--)
					{
						int index = list3[i];
						list4.Insert(0, list[index]);
						list.RemoveAt(index);
					}
					if (list4.Count > 1)
					{
						list2.Add(list4);
					}
				}
				foreach (List<KeyValuePair<double, Wall>> list5 in list2)
				{
					List<Wall> list6 = new List<Wall>();
					foreach (KeyValuePair<double, Wall> keyValuePair2 in list5)
					{
						list6.Add(keyValuePair2.Value);
					}
					arcWalls.Add(list6);
				}
			}
		}

		private void FindLinkedArcWalls(int currentIndex, int findIndex, List<KeyValuePair<double, Wall>> sortWallList, ref List<int> findIndexList)
		{
			if (currentIndex >= sortWallList.Count - 1 || findIndex >= sortWallList.Count)
			{
				return;
			}
			double val = Math.Abs(sortWallList[findIndex].Key - sortWallList[currentIndex].Key);
			double val2 = (sortWallList[findIndex].Value.Width + sortWallList[currentIndex].Value.Width) / 2.0;
			if (!Geometry.IsEqual(val, val2))
			{
				this.FindLinkedArcWalls(currentIndex, findIndex + 1, sortWallList, ref findIndexList);
				return;
			}
			Arc arc = (sortWallList[currentIndex].Value.Location as LocationCurve).Curve as Arc;
			Arc arc2 = (sortWallList[findIndex].Value.Location as LocationCurve).Curve as Arc;
			XYZ center = arc.Center;
			center.DistanceTo(arc2.GetEndPoint(0));
			XYZ endPoint = arc.GetEndPoint(0);
			XYZ endPoint2 = arc.GetEndPoint(1);
			if (Geometry.IsEqual(arc.Normal, -XYZ.BasisZ))
			{
				endPoint = arc.GetEndPoint(1);
				endPoint2 = arc.GetEndPoint(0);
			}
			XYZ endPoint3 = arc2.GetEndPoint(0);
			XYZ endPoint4 = arc2.GetEndPoint(1);
			if (Geometry.IsEqual(arc2.Normal, -XYZ.BasisZ))
			{
				endPoint3 = arc2.GetEndPoint(1);
				endPoint4 = arc2.GetEndPoint(0);
			}
			XYZ pt = center + (endPoint3 - center).Normalize() * arc.Radius;
			XYZ pt2 = center + (endPoint4 - center).Normalize() * arc.Radius;
			XYZ xyz = null;
			XYZ xyz2 = null;
			Geometry.OverlapType overlapType = Geometry.IsOverlapOfTwoArc(this.To2D(endPoint), this.To2D(endPoint2), this.To2D(center), this.To2D(pt), this.To2D(pt2), this.To2D(center), ref xyz, ref xyz2, 1E-09);
			if (overlapType == Geometry.OverlapType._OT_None || overlapType == Geometry.OverlapType._OT_Linked)
			{
				this.FindLinkedArcWalls(currentIndex, findIndex + 1, sortWallList, ref findIndexList);
				return;
			}
			findIndexList.Add(findIndex);
			this.FindLinkedArcWalls(findIndex, findIndex + 1, sortWallList, ref findIndexList);
		}

		private ExternalCommandData m_ComdData;

		private UIApplication m_UIApp;

		private UIDocument m_UIDoc;

		private Application m_App;

		private Document m_Doc;
	}
}
